﻿#region using
/*
 * Created by SharpDevelop.
 * User: Andrew
 * Date: 22/04/2013
 * Time: 22:13
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

using RRRSRoguelike.Entities;
using RRRSRoguelike.Entities.Creatures;
using RRRSRoguelike.Entities.Props;
using RRRSRoguelike.Enums;
using RRRSRoguelike.Helpers;
using RRRSRoguelike.Helpers.Actions;
using RRRSRoguelike.Managers;
using RRRSRoguelike.Screens;
#endregion

namespace RRRSRoguelike.RuleSets
{
	/// <summary>
	/// The original ruleset set out by DOM in the GDD and original tutorial.
	/// </summary>
	public class VanillaRuleSet:RuleSet
	{

		bool levelCleared = false;
		
		/// <summary>
		/// an event called when the stairs are taken to the next level.
		/// </summary>
		public event EventHandler ExitLevel = delegate{};
		/// <summary>
		/// an event called when the level is cleared of the first wave of monsters.
		/// </summary>
		public event EventHandler LevelCleared = delegate{};
		
		/// <summary>
		/// constructor
		/// </summary>
		public VanillaRuleSet():base()
		{
		}
		
		/// <summary>
		/// intialize
		/// </summary>
		public override void Initialize()
		{
			base.Initialize();
			
			AddStats();
			
			GameManager.Instance.ActionManager.ActionMap[(int)Actions.North].ActionEvent += GoNorth;
			GameManager.Instance.ActionManager.ActionMap[(int)Actions.East].ActionEvent += GoEast;
			GameManager.Instance.ActionManager.ActionMap[(int)Actions.West].ActionEvent += GoWest;
			GameManager.Instance.ActionManager.ActionMap[(int)Actions.South].ActionEvent += GoSouth;
		}

		/// <summary>
		/// add flags
		/// </summary>
		protected override void AddFlags()
		{
			// specific to jump ability
			player.Flags.Add("hasJumped", false);
		}

		/// <summary>
		/// reset bools
		/// </summary>
		protected override void ResetBools()
		{
			player.Flags["hasJumped"] = false;
		}
		
		
		void AddStats()
		{
			
			player.Stats.StatList.Add("Score",0);
			
			//Add Stats
			player.Stats.StatList.Add("MonstersKilled", 0);
			GameManager.Instance.CreatureManager.MonsterKilled += OnKill;
			
			player.Stats.StatList.Add("StepsTaken", 0);
			player.Moved += OnMove;
			
			//FIXME: this counts pickups and drops.
			player.Stats.StatList.Add("WeaponsGrabbed", 0);
			player.WeaponChanged += OnWeaponChanged;
			
		}
		
		/// <summary>
		/// do logic
		/// </summary>
		public override void DoLogic()
		{
			if (GameManager.Instance.CreatureManager.Monsters.Count == 0)
				if (!levelCleared)
			{
				LevelCleared(this,EventArgs.Empty);
				levelCleared = true;
			}
			
			base.DoLogic();
		}
		
		/// <summary>
		/// check tile the player moves to
		/// </summary>
		protected override void CheckNewTile()
		{
			//FIXME: things here should be more event driven
			base.CheckNewTile();
			
			// Should add options for auto moves
			// auto pick up sword

			AutoPick();

			// auto take stairwell
			TakeStairWell();
		}

		void AutoPick()
		{
			if (player.WeaponSlot == null)
				ActionHelper.Get(player.Position);
		}
		
		/// <summary>
		/// load content
		/// </summary>
		public override void LoadContent()
		{
			// Get Data from file
			GameManager.Instance.Dungeon.LoadData("Vanilla");
			
			base.LoadContent();
		}
		
		/// <summary>
		/// process input
		/// </summary>
		public override void ProcessInput()
		{
			var ActionManager = GameManager.Instance.ActionManager;

			processMonsters = player.Flags["hasMoved"] = GameManager.Instance.Redraw = false;

			//FIXME: we need something better than this giant load of if statements
			
			//eg this which currently does nithing but fire empty events.
			for (int i = 0; i < ActionManager.ActionMap.Count();i++)
			{
				if(ActionManager.IsActionPushed((Actions)i))
				{
						ActionManager.ActionMap[i].ActionEvent(this, new EventArgs());
				}
			}
			
			processMonsters = GameManager.Instance.Redraw = true;
			
			if(player.Flags["hasMoved"])
			{
			processMonsters = GameManager.Instance.Redraw = true;
			}
			//This checks until one is true as we can only have one action press.
			else if (ActionManager.IsActionPushed(Actions.Jump))
			{
				DoJump();
			}
			else if (ActionManager.IsActionPushed(Actions.Pick))
			{
				ActionHelper.Get(player.Position);
				processMonsters = player.Flags["hasMoved"] = false;
			}
			else if (ActionManager.IsActionPushed(Actions.Drop))
			{
				ActionHelper.Put(player.Position);
				processMonsters = player.Flags["hasMoved"] = false;
			}
			else if (ActionManager.IsActionPushed(Actions.Look))
			{
				DoLook();
				processMonsters = player.Flags["hasMoved"] = false;
			}
			else if (ActionManager.IsActionPushed(Actions.Wait))
			{
				player.Flags["hasMoved"] = false;
			}
			else if (ActionManager.IsActionPushed(Actions.Quit))
			{
				gameScreen.ShowQuitScreen();
				processMonsters = player.Flags["hasMoved"] = false;
			}
			else if (ActionManager.IsActionPushed(Actions.Help))
			{
				gameScreen.ShowHelpScreen();
				processMonsters = player.Flags["hasMoved"] = false;
			}
			else if (ActionManager.IsActionPushed(Actions.FollowToggle))
			{
				Properties.Settings.Default.FollowToggle =
					ApplicationManager.Instance.ToggleSetting(Properties.Settings.Default.FollowToggle);

				processMonsters = player.Flags["hasMoved"] = false;
			}
			else if (ActionManager.IsActionPushed(Actions.SoundToggle))
			{
				//Toggle sound
				Properties.Settings.Default.SoundToggle =
					ApplicationManager.Instance.ToggleSetting(Properties.Settings.Default.SoundToggle);

				Properties.Settings.Default.Save();

				ApplicationManager.Instance.SoundManager.PlaySound(SoundList.Pluck.ToString());

				processMonsters = player.Flags["hasMoved"] = false;
			}
			else if (ActionManager.IsActionPushed(Actions.ToggleGodMode))
			{
				//Toggle godmode
				GameManager.Instance.GodMode =
					ApplicationManager.Instance.ToggleSetting(GameManager.Instance.GodMode);

				processMonsters = player.Flags["hasMoved"] = false;

			}
			else if (ActionManager.IsActionPushed(Actions.ChangeAnimationSpeed))
			{
				ApplicationManager.Instance.ChangeAnimSpeed();

				processMonsters = player.Flags["hasMoved"] = false;
			}
			else
			{
				processMonsters = player.Flags["hasMoved"] = GameManager.Instance.Redraw = false;
			}
		}
		
		
		private void DoLook()
		{
			bool newTile = false;
			bool look = true;

			Tile lookTile = GameManager.Instance.PropManager.LookTile;

			lookTile.Active = true;
			lookTile.Position = player.Position;

			
			//FIXME Looking could be a game state.  That would move this loop.
			while (look)
			{
				var ActionManager = GameManager.Instance.ActionManager;
				
				gameScreen.GetInput();

				if (ActionManager.IsActionPushed(Actions.North))
				{ lookTile.Y -= 1; newTile = true; }
				else if (ActionManager.IsActionPushed(Actions.South))
				{ lookTile.Y += 1; newTile = true; }
				else if (ActionManager.IsActionPushed(Actions.East))
				{ lookTile.X += 1; newTile = true; }
				else if (ActionManager.IsActionPushed(Actions.West))
				{ lookTile.X -= 1; newTile = true; }
				else if (ActionManager.IsActionPushed(Actions.Quit) ||
				         ActionManager.IsActionPushed(Actions.Look))
				{
					look = false;
				}

				if (newTile)
				{
					//limit

					lookTile.Position = DungeonLevel.ClampToDungeon(lookTile.Position);

					var viewTile = (Tile)GameManager.Instance.DungeonLevel.GetTopTile(lookTile.Position);
					lookTile.Name = viewTile.Name;
					lookTile.ImageCharacter = viewTile.ImageCharacter;
					lookTile.Description = viewTile.Description;
					lookTile.Color = ConsoleColor.Blue;
					lookTile.Background = ConsoleColor.DarkCyan;

					var description = Properties.Strings.MysteryObject;

					if (viewTile.Name !=null)
						description = viewTile.Name;
					
					if (viewTile.Description != null)
					{
						description += ": ";
						description += viewTile.Description;
					}

					gameScreen.ClearMessages();
					gameScreen.ShowMessage(description,false);

					GameManager.Instance.ForceRedraw();
					newTile = false;
				}
				//Draw
			}
			lookTile.Active = false;
			GameManager.Instance.ForceRedraw();
		}
		
		private void DoJump()
		{
			//shortens calls.
			var ActionManager = GameManager.Instance.ActionManager;
			
			if (player.Flags["hasJumped"])
			{
				gameScreen.ShowMessage(Properties.Strings.Used);
				
				processMonsters = player.Flags["hasMoved"] = false;
				
			}
			else
			{

				var jump = true;
				while (jump)
				{
					var direction = gameScreen.GetDirectionFromUser();
					if (direction != Direction.Wait)
					{
						player.Flags["hasJumped"] = player.Flags["hasMoved"] = processMonsters = player.MovePlayer(direction, Constants.JumpDistance);
						//exits loop
						jump = false;
					}
					else
					{
						processMonsters = player.Flags["hasMoved"] = jump = false;
						//Allows users to see that the jump has been cancelled.
						gameScreen.ClearMessages();

					}

				}
				
			}
			
		}
		
		// This could probably be done more elegantly with events.
		bool MovePlayer(Direction direction)
		{
			gameScreen.ClearMessages();
			bool moved = player.MovePlayer(direction);
			// If the player can't move, check for bumping into doors and stuff
			if(!moved)
			{
				//Get new DungeonSquare check for door
				Point newPosition = DungeonLevel.
					GetNewPosition(player.Position, direction, 1);
				
				if (GameManager.Instance.DungeonLevel.
				    SquareContainsTile(newPosition,typeof(Door)))
				{//Open that door
					GameManager.Instance.DungeonLevel.
						GetTiles(newPosition).OfType<Door>().FirstOrDefault().Open();
					
					//FIXME: this should probably call the onmove event.  
					moved = true;
				}
			}
			return moved;
		}
		
		private void MoveToNextLevel()
		{
			player.Flags["hasJumped"] = false;
			
			levelCleared = false;

			GameManager.NextLevel();

			processMonsters = false;
			
			//TODO: Add an event an hook up a redraw
			//ReDraw();
			gameScreen.DrawTitle();
		}
		
		private void TakeStairWell()
		{
			if (GameManager.Instance.DungeonLevel.GetTile(player.Position, Layers.Prop) is Stairwell)
			{
				ExitLevel(this,EventArgs.Empty);
				
				//HACK: this should be part of a separate scoring system
				player.Stats.StatList["Score"] += GameManager.Instance.CurrentDungeonLevel *100;
				
				
				//check for victory
				if (GameManager.Instance.CurrentDungeonLevel >= 5)
				{
					GameManager.Instance.gameOver = true;
					//HACK: this should be part of a separate scoring system
					player.Stats.StatList["Score"] += 1000;
				}
				else
				{
					MoveToNextLevel();
				}
			}
		}
		
		void OnMove(object sender, EventArgs e)
		{
			//This is an attempt at the new stats implementation
			
			player.Stats.StatList["StepsTaken"]++;
			player.Flags["hasMoved"] = processMonsters = true;
		}
		
		void OnKill(object sender, EventArgs e)
		{
			//FIXME:  the start of a logger.  would be good to have a x2 or other way to show a new message the same.
			
			gameScreen.ShowMessage(player.Name + " kills " + ((Creature)sender).Name,false );
			player.Stats.StatList["MonstersKilled"]++;
			//HACK: need to cntralize the scoring ie += score.monsterkilled
			player.Stats.StatList["Score"] += GameManager.Instance.CurrentDungeonLevel*10;
		}
		
		//FIXME: use the eventargs to show if it's a new weapon etc.
		void OnWeaponChanged(object sender, EventArgs e)
		{
			player.Stats.StatList["WeaponsGrabbed"]++;
		}
		
		void GoNorth(object sender, EventArgs e)
		{
			MovePlayer(Direction.N);
		}
		
		void GoSouth(object sender, EventArgs e)
		{
			MovePlayer(Direction.S);
		}
		
		void GoWest(object sender, EventArgs e)
		{
			MovePlayer(Direction.W);
		}
		
		void GoEast(object sender, EventArgs e)
		{
			MovePlayer(Direction.E);
		}
		
	}
}
